# -*- coding: utf-8 -*-
__author__ = 'lztkdr'
__date__ = '2019/7/30 15:41'


from scrapy.http import Request
from GuaZi_Spider.items import *
import re, requests, urllib.parse, time, datetime
from utils.proxy_helper import getProxyHost
from utils.js_util import decrypt_anti_info_cookie
from fake_useragent import UserAgent

# orm 相关
from orm.orm_base import ORM_BASE,ORM_Engine
from sqlalchemy.orm import  sessionmaker

# 通过 ORM_BASE.metadata.create_all(ORM_Engine) 创建对应的表
# Base.metadata.create_all(engine)  所在的python 文件里，所有要创建的表的实体类，必须得导进来！！！
from orm import car_crawl_orm


from scrapy_redis.spiders import RedisSpider,RedisCrawlSpider

# class GuaziRedisSpider(scrapy.Spider):    #单机版    需要带上 start_urls
class GuaziRedisSpider(RedisSpider):        #分布式, 不需要显示写明 start_urls, start_urls通过redis统一分配

	name = 'guazi_redis'
	allowed_domains = ['www.guazi.com']
	# start_urls = ['https://www.guazi.com/www/buy']

	def __init__(self):
		'''
		构造函数，爬虫的初始化操作
		'''
		scrapy.Spider.__init__(self, self.name)

		self.orm_session = None
		try:
			# 先删除表
			ORM_BASE.metadata.drop_all(ORM_Engine)
			# 根据同步表
			ORM_BASE.metadata.create_all(ORM_Engine)
			# 定义数据库会话session
			self.orm_session = sessionmaker(ORM_Engine)()
		except Exception as ex:
			print(f"数据库session设置出错。出错原因：{ex}")


		# 输入关键字
		self.keyword = "现代"
		# self.keyword = input("开始起动瓜子网爬虫,请输入要爬取的关键字:")
		print(f"开始起动瓜子网爬虫,请输入要爬取的关键字:{self.keyword}")

		# 对搜索 关键字 编码
		url_encoding_keyword = urllib.parse.quote(self.keyword)
		self.search_keyword_url = f"https://www.guazi.com/www/buy/_{url_encoding_keyword}/?input=1"

		# 定义 有 2种 方式 实现（mode = 1 : 只使用 scrapy 爬取 、mode = 2：使用 scrapy + selenium 爬取）
		# 修改mode 值 改变 爬取方式！！
		self.mode = 1

		# 是否要 启用 代理
		self.enabled_proxy = True

		# 初始化默认值
		self.proxy_host = None
		self.proxies = None
		self.useragent = None
		self._antipas_cookie = None
		self.last_update_time = None  # 最后更新 antipas_cookie 的时间
		self.driver = None

		if self.mode == 1:  # 只使用 scrapy 爬取
			# 初始化 设置代理 以及 设置antipas_cookie
			self.init_proxy_antipas()

		if self.proxy_host:
			print(f"获取到代理：{self.proxy_host}")

		if self._antipas_cookie:
			print(f"antipas_cookie:{self._antipas_cookie}")


	@property
	def antipas_cookie(self):
		"""
		维护 代理池、ua 池， 每个代理IP 关联要 antipas_cookie 同时进行更新
		:return:
		"""
		cookie = self._antipas_cookie
		curr_time = datetime.datetime.now()
		if (curr_time - self.last_update_time).seconds > 50:  # 代理时效为1分钟，这里 快到失效期 则 更新
			self.init_proxy_antipas()
			cookie = self._antipas_cookie
			print("自动更换 proxy/useragent ......")
		return cookie

	def init_proxy_antipas(self):
		"""
		访问第1次通过正则拿到变化的 anti_info 值并解密得到每次访问的 cookie 值
		:return:
		"""
		while True:
			try:
				# 设置 userAgent
				self.useragent = UserAgent().chrome

				# 发现：第1次请求 和 其他继续的请求 必须是同一个 userAgent ！！！, 所以这里 和 scrapy 默认的 userAgent 相同，不需使用 fake_useragent
				first_headers = {
					'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
					'User-Agent': self.useragent
				}

				# 是否启用代理
				if self.enabled_proxy:
					# 使用代理
					self.proxy_host = getProxyHost()
					if self.proxy_host:
						self.proxies = {'http': "http://" + self.proxy_host, 'https': "https://" + self.proxy_host}

				# 获取 antipas的cookie
				resp = requests.get(self.search_keyword_url, params=None, proxies=self.proxies, headers=first_headers)
				anti_info = re.findall(r".*(var value=anti.*;)", resp.text)[0]
				cookie_info = decrypt_anti_info_cookie(anti_info)
				# 设置 antipas 的cookie
				self._antipas_cookie = cookie_info
				self.last_update_time = datetime.datetime.now()
				break
			except Exception as ex:
				print(ex)
				time.sleep(2)
				continue

	def parse(self, response):
		url = response.url
		try:
			if response.status != 200:
				# 可能 ip 被封 需要使用代理。
				print("可能 ip 被封 需要使用代理!!!")
				pass
			else:
				items = response.css("ul.carlist > li")
				if items:
					for item in items:
						detail_url = "https://www.guazi.com" + item.xpath("./a/@href").extract_first()
						print("详情页：" + detail_url)
						req = Request(detail_url,
						              meta={'type': "get_detail"},
						              # 设置 标记数据，用于 下载中间件 的 process_request 请求前 获取标记数据
						              callback=self.parse_detail,
						              dont_filter=True)  # 表示调度程序不应过滤此请求。当您想要多次执行相同的请求时，可以使用此选项来忽略重复过滤器。小心使用它，否则您将进入爬行循环。默认为False。
						yield req

				next_page_href = response.css(".pageBox .pageLink .next::attr(href)").extract_first()
				# /www/buy/o2c-1/_大众/#bread
				if next_page_href:
					next_page_href = "https://www.guazi.com" + next_page_href
					# 打印下一页 页码：
					next_page_num = int(re.search('o(\d+)c', next_page_href).group().strip('oc'))
					print(f"下一页({next_page_num})：" + next_page_href)

					req = Request(next_page_href,
					              meta={'type': "nextpage", 'url': next_page_href},
					              # 设置 标记数据，用于 下载中间件 的 process_request 请求前 获取标记数据
					              callback=self.parse,
					              dont_filter=True)  # 表示调度程序不应过滤此请求。当您想要多次执行相同的请求时，可以使用此选项来忽略重复过滤器。小心使用它，否则您将进入爬行循环。默认为False。
					yield req
		except Exception as ex:
			print(f"parse_search_keyword出现异常({url})：{ex}")
			# req = Request(url,
			#               meta= response.request.meta,
			#               callback=self.parse_search_keyword,
			#               dont_filter=True)
			yield response.request

	def parse_detail(self, response):
		url = response.url  # 详情url
		try:
			title = response.css(".product-textbox .titlebox::text").extract_first().strip()  # 标题
			licensing_time = response.css(".basic-eleven .one .typebox::text").extract_first()  # 上牌时间
			mileage = response.css(".basic-eleven .two .typebox::text").extract_first()  # 表显里程
			displacement = response.css(".basic-eleven .six .typebox::text").extract_first()  # 排量
			gearbox = response.css(".basic-eleven .five .typebox::text").extract_first()  # 变速箱
			img_src = response.css(".js-bigpic[data-index='0'] img:first-child::attr(src)").extract_first()
			sale_price = response.css(".pricebox .pricestype::text").extract_first().strip()  # 价格
			year_check = response.css(".basic-eleven .nine .typebox::text").extract_first()  # 年检到期
			insurance = response.css(".basic-eleven .ten .typebox::text").extract_first()  # 交强险
			store = response.css(".baomai-right h3::text").extract_first()  # 门店
			look_addr = response.css(".baomai-right p::text").extract_first()  # 看车地点
			owner = response.css(".people-infor span[class='f20']::text").extract_first()  # 车主

			car = CarItem(detail_url=url, title=title, licensing_time=licensing_time, mileage=mileage,
			              displacement=displacement, gearbox=gearbox, img_src=img_src, sale_price=sale_price,
			              year_check=year_check,
			              insurance=insurance, store=store, look_addr=look_addr, owner=owner
			              )
			print(car)
			print(
				"---------------------------------------------------------------------------------------------------------------------------------------------------")
			yield car
		except Exception as ex:
			print(f"parse_detail出现异常({url})：{ex}")
			req = Request(url,
			              meta={'type': "get_detail"},
			              callback=self.parse_detail,
			              dont_filter=True)
			yield req

